Representational State Transfer (REST): Web Architecture Style
Understand the constraints of REST architecture style and its purpose.
Motivation #
In December 1990, Tim Berners-Lee had a vision for information sharing and started a World Wide Web (or simply the web) project. Initially, during the World Wide Web’s design phase, the web was for a limited number of people; however, when it was opened to the public, the number of users started multiplying and increased to 40 million within five years. Due to the increasing number of users, the web was on the way to collapse because the users' traffic was outgrowing the capacity of the Internet infrastructure. This behavior forced the software architects to design an efficient web architecture to make the web flexible, performant, and scalable to handle ever-increasing traffic. In this regard, the REST architecture style was proposed, and it plays a vital role in designing a scalable system.
Note: “All design decisions at the architectural level should be made within the context of the functional, behavioral, and social requirements of the system being designed, which is a principle that applies equally to both software architecture and the traditional field of building architecture.”
— Roy Fielding (The author of the Ph.D. dissertation that originally presented REST.)
Representational state transfer (REST)#
REST is a well-known web architecture style proposed by Roy Fielding in 2000 to solve the web's scalability problem. Roy Fielding identified that the web's scalability could be directed by certain key constraints. These constraints are collectively called the web's architectural style. Further, these constraints also act as a backbone for designing REST APIs.
Hence, in order to scale the Internet, a pragmatic approach is adopted to satisfy all the following constraints:
Client-server
Cache
Stateless
Uniform interface
Layered system
Code-on-demand
We explain each constraint in detail below.
Client-server#
Client-server is the most commonly employed architectural style for network-based applications. A server component that provides several services listens for a request for those services. The client communicates with the server via the Internet (connector), waiting for a request to be processed by the service. The client's request is either rejected or fulfilled by the server, which is communicated back to the client via a response.
Separation of concerns is the core principle that focuses on client-server constraints. In order to increase scalability, the server component should be made simpler through adequate functional separation. This simplification involves consolidating all user interface features into a single client component. Both of these components can be developed (evolved) separately as long as the interface stays the same.
Cache#
A cache is an intermediate component between the client and server that contains the response to the earlier requests. These cached responses are used for equivalent and identical requests instead of directing requests to the server. The network efficiency and user's perceived performance can be improved using cache on the client's side.
Note: The response data can be cached on the client's side and utilized for similar/equivalent requests.
This constraint requires that the response data be explicitly or implicitly marked cacheable or non-cacheable. If the data in the response is marked cacheable, the client's cache can reuse them later for similar requests.
The major tradeoff with the cache constraint is that it can reduce reliability and consistency if stale data in the cache varies considerably from the data received in the direct request to the server.
Point to Ponder
Question
What could be the approach to check the validity of the data (or a web page) in the cache?
There are a few ways to check whether the page needs to be fetched again.
-
One method is to use the
ExpiresHTTP header in the response that would determine when the page needs to be fetched again. -
Another approach is to use some heuristics. For example, if a page is not modified in the last year—indicated by the
Last-ModifiedHTTP header—it is improbable that it will change in the next hour. However, such heuristics should be used with extra care. -
Another helpful approach is to use conditional
GET. Through this request, the server returns a short reply if the cached page is still valid; otherwise, the server sends the complete response.Let’s look at the conditional
GETrequest as given below.GET /items.html HTTP/1.1
Host: www.example.com
If-Modified-Since: Fri, 14 Aug 2022
15:35:47 GMTIn the
GETrequest above, theIf-Modified-SinceHTTP header makes theGETrequest conditional. TheIf-Modified-Sincestatement is self-explanatory, asking for a page from the server modified afterFri, 14 Aug, 2022 15:35:47 GMT.
Stateless#
This constraint refers to the fact that no session state is allowed on the server; it should be entirely kept on the client. Moreover, each request from the client should contain all the necessary information for the server to process the request.
Note: The server should not store any data from the previous requests.
The stateless constraint improves the following performance measures:
Reliability is increased because it allows the server to recover from partial failures easily, and no data loss occurs for the users. For example, if there is no data kept on the server, then server failure wouldn't disturb the client's side activity or any previous requests from the client.
Scalability is improved because no user data is stored on the server, so the data is immediately removed after processing a request, which frees the resources for other incoming requests. For example, in this case, the server can handle more requests than it could have handled while keeping the limited users' data.
Visibility is improved because the server does not have to look for extra information rather than the data in a single request to determine its entire nature. For example, if a request needs to retrieve data from the database, it will contain all the necessary information to initiate database queries. So a monitoring system can easily determine the purpose of a request.
Like other architectural options, this constraint also has a tradeoff. By sending repeating data in a succession of requests (since the data/information can’t be kept on the server), it might reduce the network’s performance.
Uniform interface#
Since a system is composed of several components, for a system to work in the desired way, the components' interaction should be directed by a uniform interface. By applying the software engineering concept of generality to the component interface, the overall system design may be made simpler and the visibility of how these components interact can be enhanced. Independent development of the components can be increased by decoupling their services from their implementation.
Note: The exchange of data should be standardized between the components.
The disadvantage of the uniform interface is that it might degrade efficiency since the information has to be transferred in a standardized form rather than the format required by the application. On the other hand, if any of the components will not follow the standards, the web's communication system will break.
In addition, the following architectural constraints are proposed to guide the behavior of the components:
Identification of resources: In REST, the abstraction of information is a resource. Any information that can be named is a resource—for example, a document, image, video, or a collection of other resources or objects. These resources are addressed by a unique identifier called a Uniform Resource Identifier (URI). For example, the URI uniquely identifies a specific website's root resource.
Manipulation of resources through representations: Representation of data is the sequence of bytes and the relevant metadata. In this constraint, the clients have the liberty to manipulate the representation of resources. In other words, the same exact source (documents and objects) can be represented to different clients in different ways. For example, a web page can be rendered as an HTML page in a browser while it is shown in XML or JSON format in a console program.
Self-descriptive messages: This is another constraint that ensures a uniform interface between different components. A self-descriptive (response) message contains all the information the client needs to understand—for example,
200 OKindicates the successful execution of the client's request. There should be no need for extra information—documents or any other data— to understand the message.Hypermedia as the engine of application state (HATEOAS): A client can request information dynamically through hypermedia. Hypermedia is a nonlinear media of information containing different types of resources, including images, videos, hyperlinks, etc. The resources requested from the server should also contain links to other related resources. Links are the components that allow users to traverse information in a relevant and directed way.
Layered system#
The layered system constraints imposed an organized hierarchy of an intermediate set of components (referred to as a layer) between the client and the server. This constraint enables client-server communication to be intercepted by other essential components to make the communication easier, secure, and properly directed to the intended servers.
Note: Adding more components between client and server assists and directs the communication between them.
The layered approach enhances the decoupling across multiple layers by making it visible to only the adjacent layers and hiding it from the other layers. For example, in the figure below, only proxy and downstream servers know that the request goes through the load balancers, but the clients might be unaware of it.
The layered system constraint improves the following:
Evolvability because each layer can be modified or added with advanced functionality without affecting other layers.
Reusability because the existing components that can be reused in one form or the other instead of reinventing or creating from scratch.
An example of a layered system is adding a proxy as well as load balancing and security checking components between client-server communication.
One of the major drawbacks of the layered system is that the requests (or responses) might need to traverse more hops, which usually adds latency.
Code-on-demand#
This constraint enables web servers to send executable programs (code) to clients, such as plug-ins, scripts, applets, and so on. Code-on-demand establishes a technological coupling between clients and servers. The client application must understand the code it downloads from the servers; therefore, this constraint is the only one in the web's architectural style that is considered optional. For example, certain applications require additional plug-ins to be installed on the client side. Therefore, such constraints can't be made compulsory in the REST architecture.
Note: A client application can request servers for codes (scripts, applets, plug-ins) that can be executed on the client side.
The major advantages of the code-on-demand are:
Extensibility: The client's extensibility is improved because it has the independence of how certain things can be done.
Performance: The user's perceived performance is improved because certain things happen locally on the client's side instead of regular interaction with the server.
One drawback of this constraint is that scripts sent from the server may contain malicious code that could interfere with the client's processes and might interrupt other activities. For this purpose, a sandbox on the client side is used to avoid the potentially harmful activities of the script. In the sandbox, the code is executed in an isolated and safe environment where it’s being observed for harmful activity, which can be blocked on the spot, and the user is notified accordingly.
Another drawback is that the monitoring system may be unable to track the request's full nature due to the server sending code instead of simple data.
Note: Adhering to all six constraints is only possible in an ideal scenario. As we have seen, some of the constraints are conflicting—for example, if we conform to the stateless constraint, we can achieve visibility; however, at the same time, using the code-on-demand constraint reduces the visibility of the full nature of the request. Therefore, API designers should follow the maximum possible constraints while designing a RESTful web API.
REST Constraints Overview
Constraints | Features | Drawbacks |
Client-server |
|
|
Cache |
|
|
Stateless |
|
|
Uniform interface |
|
|
Layered system |
|
|
Code-on-demand |
|
|
Point to Ponder
Question
What is the difference between REST and HTTP?
REST is a web architecture style that proposes some constraints that should be used to design web or mobile applications. However, HTTP is the protocol, or, in simple words, a method that enables us to achieve the constraints proposed by REST. Although HTTP was designed earlier than REST, fortunately, it has all the features that can be utilized to make the World Wide Web conform to the REST constraints. Therefore, HTTP is widely adopted in this regard.
Introduction to Web API Architectural Styles
RESTful API